/**
 * @file    elog_flash.c
 * @brief   EasyLogger Flash插件实现
 * @details 本文件实现了EasyLogger Flash存储插件的核心功能，包括：
 *          - 基于EasyFlash库的Flash日志存储
 *          - 支持缓冲模式和直写模式两种工作方式
 *          - Flash日志的写入、读取、清除和过滤功能
 *          - Flash存储锁定和解锁控制机制
 *          - Flash插件的初始化和反初始化流程
 *          - 自动字对齐处理和缓冲区管理
 *          
 *          功能特性：
 *          - 缓冲模式：减少Flash写入次数，延长Flash寿命
 *          - 直写模式：数据立即持久化，防止断电丢失
 *          - 多线程安全：提供锁定机制保护数据一致性
 *          - 灵活配置：支持缓冲区大小和存储地址配置
 *          - 异常处理：支持在系统异常时禁用锁定输出日志
 *          
 *          依赖库：必须使用EasyFlash库 (https://github.com/armink/EasyFlash)
 *          EasyFlash是一个轻量级的嵌入式Flash存储器库，提供了：
 *          - 键值数据库功能
 *          - 日志存储功能  
 *          - 启动参数存储功能
 *          - 支持多种Flash芯片
 */

#define LOG_TAG    "elog.flash"    /* 日志标签 */

/* ------------------------------------ 包含文件 -------------------------------------- */
#include "elog_flash.h"  /* Flash插件头文件 */
#include <easyflash.h>    /* EasyFlash库头文件 */
#include <stdio.h>        /* 标准输入输出库 */
#include <stdlib.h>       /* 标准库函数 */
#include <string.h>       /* 字符串处理函数 */

/* ----------------------------------- 全局变量定义 ----------------------------------- */
#ifdef ELOG_FLASH_USING_BUF_MODE
static char log_buf[ELOG_FLASH_BUF_SIZE] = { 0 };                  /* Flash日志缓冲区 */
static size_t cur_buf_size = 0;                                    /* 当前缓冲区写入位置 */
#endif

static bool init_ok = false;                                       /* 初始化完成标志 */
static bool log_buf_lock_enabled = true;                           /* Flash日志缓冲区锁定启用标志，默认启用 */
static bool log_buf_is_locked_before_enable = false;               /* 启用前缓冲区锁定状态 */
static bool log_buf_is_locked_before_disable = false;              /* 禁用前缓冲区锁定状态 */

/* ----------------------------------- 静态函数声明 ----------------------------------- */
static void log_buf_lock(void);
static void log_buf_unlock(void);

/**
 * @brief   初始化Flash插件
 * @return  错误代码
 * @retval  ELOG_NO_ERR 初始化成功
 * @note    该函数会初始化移植层接口并配置缓冲区
 */
ElogErrCode elog_flash_init(void)
{
    ElogErrCode result = ELOG_NO_ERR;

    /* 检查缓冲区大小必须为字对齐 */
    ELOG_ASSERT(ELOG_FLASH_BUF_SIZE % 4 == 0);

#ifdef ELOG_FLASH_USING_BUF_MODE
    /* 初始化当前Flash日志缓冲区写入位置 */
    cur_buf_size = 0;
#endif

    /* 移植层初始化 */
    elog_flash_port_init();
    
    /* 标记初始化完成 */
    init_ok = true;

    return result;
}

/**
 * @brief   输出指定位置和大小的Flash日志
 * @param   index Flash中日志的起始索引位置，会自动进行4字节对齐
 * @param   size  要输出的日志数据大小
 * @note    索引范围：0到(Flash已使用总大小-1)
 */
void elog_flash_output(size_t index, size_t size)
{
    uint32_t buf[32] = { 0 };                                     /* 128字节缓冲区 */
    size_t log_total_size = ef_log_get_used_size();
    size_t buf_size = sizeof(buf);
    size_t read_size = 0, read_overage_size = 0;

    /* 对索引进行字对齐处理 */
    index = index / 4 * 4;
    
    /* 检查输出位置和大小是否越界 */
    if (index + size > log_total_size) 
    {
        log_i("The output position and size is out of bound. The max size is %d.", log_total_size);
        return;
    }
    
    /* 必须在初始化完成后调用此函数 */
    ELOG_ASSERT(init_ok);
    
    /* 锁定Flash日志缓冲区 */
    log_buf_lock();
    
    /* 循环输出Flash中保存的日志 */
    while (true) 
    {
        if (read_size + buf_size < size) 
        {
            /* 读取完整缓冲区大小的数据 */
            ef_log_read(index + read_size, buf, buf_size);
            elog_flash_port_output((const char*)buf, buf_size);
            read_size += buf_size;
        } 
        else 
        {
            /* Flash读取需要字对齐处理 */
            if ((size - read_size) % 4 == 0) 
            {
                read_overage_size = 0;
            } 
            else 
            {
                read_overage_size = 4 - ((size - read_size) % 4);
            }
            
            /* 读取剩余数据 */
            ef_log_read(index + read_size, buf, size - read_size + read_overage_size);
            elog_flash_port_output((const char*) buf + read_overage_size, size - read_size);
            
            /* 输出换行符 */
            elog_flash_port_output(ELOG_NEWLINE_SIGN, strlen(ELOG_NEWLINE_SIGN));
            break;
        }
    }
    
    /* 解锁Flash日志缓冲区 */
    log_buf_unlock();
}

/**
 * @brief   输出Flash中的所有日志
 * @note    等价于调用elog_flash_output(0, ef_log_get_used_size())
 */
void elog_flash_output_all(void)
{
    /* 从索引0开始输出所有日志 */
    elog_flash_output(0, ef_log_get_used_size());
}

/**
 * @brief   输出Flash中最近的指定大小日志
 * @param   size 要输出的最近日志数据大小
 * @note    常用于快速查看最近的系统运行状态
 */
void elog_flash_output_recent(size_t size)
{
    size_t max_size = ef_log_get_used_size();

    /* 检查输出大小是否为0 */
    if (size == 0) 
    {
        return;
    }

    /* 检查输出大小是否超出范围 */
    if (size > max_size) 
    {
        log_i("The output size is out of bound. The max size is %d.", max_size);
    } 
    else 
    {
        /* 从最近位置开始输出指定大小的日志 */
        elog_flash_output(max_size - size, size);
    }
}

/**
 * @brief   将日志写入Flash存储
 * @param   log  指向日志内容的指针
 * @param   size 日志数据的字节大小
 * @note    支持缓冲模式和直写模式，缓冲模式可减少Flash写入次数
 */
void elog_flash_write(const char *log, size_t size)
{
#ifdef ELOG_FLASH_USING_BUF_MODE
    size_t write_size = 0, write_index = 0;
#else
    size_t write_size_temp = 0;
    EfErrCode result = EF_NO_ERR;
    char write_overage_c[4] = { '\r', '\r', '\r', '\r' };          /* 用于字对齐的填充字符 */
#endif

    /* 必须在初始化完成后调用此函数 */
    ELOG_ASSERT(init_ok);

    /* 锁定Flash日志缓冲区 */
    log_buf_lock();

#ifdef ELOG_FLASH_USING_BUF_MODE
    /* 缓冲模式：先写入RAM缓冲区 */
    while (true) 
    {
        if (cur_buf_size + size > ELOG_FLASH_BUF_SIZE) 
        {
            /* 缓冲区空间不足，先填满当前缓冲区 */
            write_size = ELOG_FLASH_BUF_SIZE - cur_buf_size;
            elog_memcpy(log_buf + cur_buf_size, log + write_index, write_size);
            write_index += write_size;
            size -= write_size;
            cur_buf_size += write_size;
            
            /* 解锁Flash日志缓冲区 */
            log_buf_unlock();
            
            /* 将所有缓冲的日志写入Flash，cur_buf_size将被重置 */
            elog_flash_flush();
            
            /* 重新锁定Flash日志缓冲区 */
            log_buf_lock();
        } 
        else 
        {
            /* 缓冲区空间足够，直接写入 */
            elog_memcpy(log_buf + cur_buf_size, log + write_index, size);
            cur_buf_size += size;
            break;
        }
    }
#else
    /* 直写模式：直接写入Flash */
    
    /* 计算字对齐写入大小 */
    write_size_temp = size / 4 * 4;
    
    /* 将日志写入Flash */
    result = ef_log_write((uint32_t *) log, write_size_temp);
    
    /* 写入最后的字对齐数据 */
    if ((result == EF_NO_ERR) && (write_size_temp != size)) 
    {
        elog_memcpy(write_overage_c, log + write_size_temp, size - write_size_temp);
        ef_log_write((uint32_t *) write_overage_c, 4);
    }
#endif

    /* 解锁Flash日志缓冲区 */
    log_buf_unlock();
}

#ifdef ELOG_FLASH_USING_BUF_MODE
/**
 * @brief   将缓冲区中的所有日志数据刷新到Flash存储
 * @note    强制将RAM缓冲区中的所有日志数据立即写入Flash存储
 */
void elog_flash_flush(void)
{
    size_t write_overage_size = 0;

    /* 必须在初始化完成后调用此函数 */
    ELOG_ASSERT(init_ok);
    
    /* 锁定Flash日志缓冲区 */
    log_buf_lock();
    
    /* Flash写入需要字对齐 */
    if (cur_buf_size % 4 != 0) 
    {
        write_overage_size = 4 - (cur_buf_size % 4);
    }
    
    /* 用'\r'填充以实现字对齐 */
    memset(log_buf + cur_buf_size, '\r', write_overage_size);
    
    /* 将所有缓冲的日志写入Flash */
    ef_log_write((uint32_t *) log_buf, cur_buf_size + write_overage_size);
    
    /* 重置位置 */
    cur_buf_size = 0;
    
    /* 解锁Flash日志缓冲区 */
    log_buf_unlock();
}
#endif

/**
 * @brief   清除Flash和RAM缓冲区中的所有日志数据
 * @note    此操作不可逆，所有历史日志将永久丢失
 */
void elog_flash_clean(void)
{
    EfErrCode clean_result = EF_NO_ERR;

    /* 必须在初始化完成后调用此函数 */
    ELOG_ASSERT(init_ok);
    
    /* 锁定Flash日志缓冲区 */
    log_buf_lock();
    
    /* 清除Flash中的所有日志 */
    clean_result = ef_log_clean();

#ifdef ELOG_FLASH_USING_BUF_MODE
    /* 重置位置 */
    cur_buf_size = 0;
#endif

    /* 解锁Flash日志缓冲区 */
    log_buf_unlock();

    /* 输出清除操作结果 */
    if(clean_result == EF_NO_ERR) 
    {
        log_i("All logs which in flash is clean OK.");
    } 
    else 
    {
        log_e("Clean logs which in flash has an error!");
    }
}

/**
 * @brief   启用或禁用Flash插件的锁定机制
 * @param   enabled 锁定控制标志：true-启用，false-禁用
 * @note    禁用锁定机制不推荐，除非需要在系统异常时输出日志
 */
void elog_flash_lock_enabled(bool enabled)
{
    log_buf_lock_enabled = enabled;
    
    /* 在启用日志缓冲区锁之前将重新锁定或解锁 */
    if (log_buf_lock_enabled) 
    {
        if (!log_buf_is_locked_before_disable && log_buf_is_locked_before_enable) 
        {
            /* 禁用前日志缓冲区锁是解锁的，启用后锁将解锁 */
            elog_flash_port_lock();
        } 
        else if (log_buf_is_locked_before_disable && !log_buf_is_locked_before_enable) 
        {
            /* 禁用前日志缓冲区锁是锁定的，启用后锁将锁定 */
            elog_flash_port_unlock();
        }
    }
}

/**
 * @brief   锁定Flash日志缓冲区
 * @note    静态函数，仅供模块内部使用
 */
static void log_buf_lock(void)
{
    if (log_buf_lock_enabled) 
    {
        elog_flash_port_lock();
        log_buf_is_locked_before_disable = true;
    } 
    else 
    {
        log_buf_is_locked_before_enable = true;
    }
}

/**
 * @brief   解锁Flash日志缓冲区
 * @note    静态函数，仅供模块内部使用
 */
static void log_buf_unlock(void)
{
    if (log_buf_lock_enabled) 
    {
        elog_flash_port_unlock();
        log_buf_is_locked_before_disable = false;
    } 
    else 
    {
        log_buf_is_locked_before_enable = false;
    }
}
